﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Media.Imaging;
using System.Security.Cryptography;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;

namespace Pasta.Medio.Models
{
	public class MediaImportModel
	{
		IMedio _view;

		// [Dependency]
		Settings _settings;

		private BackgroundWorker renameWorker;

		public MediaImportModel(IMedio view)
		{
			_view = view;

			// [injection]
			_settings = Services.Settings;

			// mappingWorker
			renameWorker = new BackgroundWorker();
			renameWorker.WorkerReportsProgress = true;
			renameWorker.WorkerSupportsCancellation = true;
			renameWorker.DoWork += renameWorker_DoWork;
			renameWorker.ProgressChanged += mappingWorker_ProgressChanged;
			renameWorker.RunWorkerCompleted += mappingWorker_RunWorkerCompleted;
		}

		public List<Media> ListMedia(string folder, Enums.MediaType mediaType, string pattern)
		{
			List<Media> media = null;
			var exts = mediaType == Enums.MediaType.Photo ? _settings.PhotoTypes : _settings.VideoTypes;
			if (Directory.Exists(folder))
			{
				media = new List<Media>();
				_media.Clear();
				foreach (string ext in exts)
				{
					foreach (string file in Directory.EnumerateFiles(folder, ext, SearchOption.AllDirectories))
					{
						FileInfo fi = new FileInfo(file);
						Media m = CreateMediaInfo(fi);

						DateTime dt = (m.Shot.HasValue && m.Shot.Value <= m.Modified) ? m.Shot.Value : m.Modified;
						string target = dt.ToString(pattern);
						int count = media.Count(md => md.Target.StartsWith(target));
						if (File.Exists(Path.Combine(folder, target + fi.Extension)))
						{
							count++;
						}
						if (count > 0)
						{
							target += String.Format("-{0}", count);
						}
						m.Target = target + fi.Extension;

						_media.Add(m);
					}
				}
			}
			return media;
		}

		public Media CreateMediaInfo(FileInfo fi)
		{
			Media media = null;
			if (fi != null)
			{
				media = new Media { Selected = true, Source = fi.FullName };
				if (IsPhoto(fi))
				{
					using (FileStream fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
					{
						BitmapSource img = BitmapFrame.Create(fs);
						BitmapMetadata md = (BitmapMetadata)img.Metadata;
						media.Shot = DateTime.Parse(md.DateTaken);
						media.Modified = FileDate(fi);
						string[] parts = md.CameraModel.Split(new char[] { ',' });
						media.Camera = parts[parts.Length - 1].Trim();
						media.Size = fi.Length;
						media.Sha1 = CalculateSHA1(fs);
					}
				}
				else if (IsVideo(fi))
				{
					using (FileStream fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
					{
						media.Modified = FileDate(fi);
						media.Size = fi.Length;
						media.Sha1 = CalculateSHA1(fs);
					}
				}
			}
			return media;
		}


		public DateTime FileDate(FileInfo f)
		{
			DateTime dt = f.CreationTime;
			if (f.LastAccessTime < dt) dt = f.LastAccessTime;
			if (f.LastWriteTime < dt) dt = f.LastWriteTime;
			return dt;
		}

		public bool IsPhoto(FileInfo fi)
		{
			return fi != null && _settings.PhotoTypesString.IndexOf(fi.Extension.ToLower()) >= 0;
		}

		public bool IsVideo(FileInfo fi)
		{
			string types = String.Join("", _settings.VideoTypes).Replace("*", "");
			return fi != null && _settings.VideoTypesString.IndexOf(fi.Extension.ToLower()) >= 0;
		}

		public static string CalculateSHA1(FileStream fs)
		{
			byte[] hash = SHA1.Create().ComputeHash(fs);
			return Convert.ToBase64String(hash);
		}

		private bool _restartRenamer = false;
		protected volatile List<Media> _media = new List<Media>();
		private long _ticks;

		public void CopyRenameMedia(List<Media> media = null)
		{
			// fetch the media when restarted
			//_media = media;
			if (_media == null)
			{
				return;
			}

			// if running, stop and restart the worker
			if (renameWorker.IsBusy)
			{
				_restartRenamer = true;
				renameWorker.CancelAsync();
				return;
			}

			_ticks = DateTime.Now.Ticks;

			renameWorker.RunWorkerAsync(_media);

		}

		public void StopCopyRename()
		{
			if (renameWorker.IsBusy)
			{
				_restartRenamer = false;
				renameWorker.CancelAsync();
				// a ResetEvent would block the UI thread which includes the
				// Completed method called by the worker -> dead lock
				while (renameWorker.IsBusy)
				{
					Application.DoEvents();
					Thread.Sleep(100);
				}
			}
		}

		private void renameWorker_DoWork(object sender, DoWorkEventArgs e)
		{
			BackgroundWorker bw = sender as BackgroundWorker;

			List<Media> media = e.Argument as List<Media>;
			if (_media == null) return;
			int numFiles = _media.Count(m => m.Selected);
			if (numFiles == 0) return;

			int success = 0;
			int fail = 0;
			int index = 0;
			foreach (var m in _media.Where(m => m.Selected))
			{
				if (bw.CancellationPending)
				{
					e.Cancel = true;
					return;
				}
				string target = Path.Combine(_settings.StagingFolder, m.Target);
				try
				{
					string s = String.Format("Rename {0} to {1}", m.Source, m.Target);
					_view.UpdateStatus(s, 1);
					Thread.Sleep(1500);
					//FileInfo fiSrc = new FileInfo(m.Source);
					//File.Copy(m.Source, target);
					//FileInfo fiDst = new FileInfo(target);
					//fiDst.LastAccessTimeUtc = fiSrc.LastAccessTimeUtc;
					//fiDst.LastWriteTimeUtc = fiSrc.LastWriteTimeUtc;
					//fiDst.CreationTimeUtc = fiSrc.CreationTimeUtc;

					// report to UI
					//ViewContainer.Invoke((MethodInvoker)delegate
					//{
					//    row["Full Genotype"] = gx;
					//    row["Resistance"] = ab;
					//});

					// report progress
					bw.ReportProgress(100 * index / numFiles);

					success++;
					m.Selected = false;
				}
				catch
				{
					fail++;
				}
				index++;
			}
		}

		private void mappingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			if (e.Cancelled && _restartRenamer)
			{
				_restartRenamer = false;
				CopyRenameMedia();
				return;
			}
			var renameTime = TimeSpan.FromTicks(DateTime.Now.Ticks - _ticks);
			//StatusBar.Panels[0].Text += String.Format(" - {0:0.00} sec", ts.TotalSeconds);
			//StatusBar.Panels[1].Visible = false;
			//BroadcastCommand(Constants.CommandNames.MappingComplete);
		}

		private void mappingWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			_view.UpdateProgress(e.ProgressPercentage);
		}
	}
}
